package yxy.game.pm2.bean.module.rank;

import java.util.*;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.redisson.api.RLock;
import org.redisson.api.RScoredSortedSet;
import org.redisson.client.protocol.ScoredEntry;

import yxy.apple.util.redisson.RedisException;
import yxy.cherry.game.base.redis.RedisManager;
import yxy.game.pm2.bean.module.BaseGameRedisBean;

abstract public class RankModule extends BaseGameRedisBean implements IRank {
    // bean===========================================================================

    private RankInfo rankInfo;

    protected void initInfo() {
        String rankKey = rankKey();
        rankInfo = RankInfo.get(rankKey);
    }

    protected String key() {
        return String.format("%s:SORT", rankInfo.getRankKey());
    }

    // func===========================================================================

    public void updatetime() {
        DateTime now = null;
        DateTime updateTime = null;
        boolean isupdate = false;
        try {
            if (tryLock()) {
                now = DateTime.now();
                updateTime = rankInfo.getUpdateTime();
                if (updateTime == null) {
                    rankInfo.setUpdateTime(now);
                    rankInfo.set();
                    return;
                }
                if (new LocalDate(now).equals(new LocalDate(updateTime))) {
                    return;
                }
                rankInfo.setUpdateTime(now);
                rankInfo.set();
                isupdate = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            unlock();
        }
        if (isupdate) {
            handleUpdate(updateTime, now);
        }
    }

    /**
     * 刷新时间时的操作
     */
    public void handleUpdate(DateTime ori, DateTime now) {

    }

    public int topSize() {
        return 100;
    }

    abstract public String rankName();

    /**
     * 数据整形
     */
    protected abstract long valuePlastic(long value);

    /**
     * 数据还原
     */
    protected abstract long valueRestore(long score);

    /**
     * 数据需要重构
     */
    protected boolean reconsitution() {
        return true;
    }

    // redis==========================================================================

    abstract protected String rankKey();

    protected RScoredSortedSet<String> touch() {
        try {
            return RedisManager.client(redis()).getScoredSortedSet(key());
        } catch (RedisException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 替换数据
     */
    public void put(String key, long value) {
        try {
            if (tryLock()) {
                addScore(key, value, touch());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            unlock();
        }
    }

    /**
     * 移除某元素
     * @param key
     */
    public void delete(String key){
        try {
            if (tryLock()) {
                RScoredSortedSet<String> zset = touch();
                Double pscore = zset.getScore(key);
                if (pscore != null) {
                    zset.remove(key);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            unlock();
        }
    }

    /**
     * 累加数据
     */
    public void addAndSet(String key, long value) {
        try {
            if (tryLock()) {
                RScoredSortedSet<String> zset = touch();
                Double pscore = zset.getScore(key);
                long ovalue = 0;
                if (pscore != null) {
                    ovalue = valueRestore(pscore.longValue());
                }
                addScore(key, value + ovalue, zset);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            unlock();
        }
    }

    protected void addScore(String key, long value) {
        addScore(key, value, touch());
    }

    private void addScore(String key, long value, RScoredSortedSet<String> zset) {
        Double pscore = zset.getScore(key);
        if (pscore != null) {
            if (value == valueRestore(pscore.longValue())) {
                // 同分不更新
                return;
            }
        }

        long v = valuePlastic(value);
        if (!reconsitution()) {
            zset.add(v, key);
            return;
        }
        long w = valuePlastic(value + 1);
        // 找到改数值区间内 排名最小的数据
        Collection<ScoredEntry<String>> entries = zset.entryRange(v, true, w, false, 0, 1);
        long minScore = 0;
        if (entries.size() <= 0) {
            // 如果该区间没有数据，说明是新数据，直接录入
            minScore = w - 1;
            zset.add(minScore, key);
            return;
        }
        for (ScoredEntry<String> entry : entries) {
            // 获取排名最低的分值
            long score = entry.getScore().longValue();
            minScore = score;
            break;
        }
        if (minScore - 1 > v) {
            minScore -= 1;
        } else {
            // 触底 判断排名是否进入敏感区
            int rank = 0;
            // 取该分数区间内的所有数据
            Collection<ScoredEntry<String>> maxentrys = zset.entryRangeReversed(v, true, w, false);
            for (ScoredEntry<String> entry : maxentrys) {
                // 获取最大分值数据的排名
                String code = entry.getValue();
                rank = zset.revRank(code);
                break;
            }
            // 排名进入敏感区域 重新排名
            if (rank < topSize()) {
                minScore = w - 1;
                for (ScoredEntry<String> entry : maxentrys) {
                    String code = entry.getValue();
                    zset.add(minScore, code);
                    if (minScore - 1 > v) {
                        minScore -= 1;
                    }
                }
            }
        }
        zset.add(minScore, key);
    }

    /**
     * 取单人排名 没有排名为0
     */
    public int rank(String key) {
        RScoredSortedSet<String> zset = touch();
        Integer rank = zset.revRank(key);
        if (rank == null) {
            return 0;
        }
        return rank + 1;
    }

    /**
     * 取单人数据
     */
    public RankEntry get(String key) {
        RScoredSortedSet<String> zset = touch();
        Double score = zset.getScore(key);
        if (score == null) {
            return null;
        }
        long value = valueRestore(score.longValue());
        return new RankEntry(key, value);
    }

    /**
     * 取排名数据
     */
    public RankEntry entryByRanking(int ranking) {
        ranking = ranking - 1;
        if (ranking < 0) {
            ranking = 0;
        }
        RScoredSortedSet<String> zset = touch();
        Collection<ScoredEntry<String>> entries = zset.entryRangeReversed(ranking, ranking);
        if (entries.size() <= 0) {
            return null;
        }
        for (ScoredEntry<String> entry : entries) {
            String key = entry.getValue();
            Double score = entry.getScore();
            long value = valueRestore(score.longValue());
            return new RankEntry(key, value);
        }
        return null;
    }

    /**
     * 根据排名列表选取元素
     */
    public Collection<RankEntry> pick(Collection<Integer> ranks) {
        ArrayList<RankEntry> list = new ArrayList<>();
        RScoredSortedSet<String> zset = touch();
        for (Integer rank : ranks) {
            int ranking = rank - 1;
            if (ranking < 0) {
                continue;
            }
            Collection<ScoredEntry<String>> entries = zset.entryRangeReversed(ranking, ranking);
            if (entries.size() <= 0) {
                continue;
            }
            for (ScoredEntry<String> entry : entries) {
                String key = entry.getValue();
                Double score = entry.getScore();
                long value = valueRestore(score.longValue());
                list.add(new RankEntry(key, value));
                break;
            }
        }
        return list;
    }

    /**
     * 最佳玩家
     */
    public RankEntry best() {
        try {
            RScoredSortedSet<String> zset = touch();
            String best = zset.last();
            long score = zset.getScore(best).longValue();
            long value = valueRestore(score);
            return new RankEntry(best, value);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 第一名
     *
     * @return
     */
    public String first() {
        try {
            RScoredSortedSet<String> zset = touch();
            String best = zset.last();
            return best;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 第二名
     *
     * @return
     */
    public String second() {
        try {
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entries = zset.entryRangeReversed(1, 1);
            if (entries.size() <= 0) {
                return null;
            }
            for (ScoredEntry<String> entry : entries) {
                String key = entry.getValue();
                return key;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 第三名
     *
     * @return
     */
    public String third() {
        try {
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entries = zset.entryRangeReversed(2, 2);
            if (entries.size() <= 0) {
                return null;
            }
            for (ScoredEntry<String> entry : entries) {
                String key = entry.getValue();
                return key;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 4到10名
     *
     * @return
     */
    public ArrayList<String> fourthToTenth() {
        try {
            ArrayList<String> list = new ArrayList<>();
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entries = zset.entryRangeReversed(3, 9);
            if (entries.size() <= 0) {
                return null;
            }
            for (ScoredEntry<String> entry : entries) {
                String key = entry.getValue();
                list.add(key);
            }
            return list;
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 取TOP集合
     */
    public Collection<RankEntry> top(int size) {
        try {
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entrys = zset.entryRangeReversed(0, size - 1);
            ArrayList<RankEntry> topList = new ArrayList<>();
            for (ScoredEntry<String> entry : entrys) {
                String entrykey = entry.getValue();
                long score = entry.getScore().longValue();
                long value = valueRestore(score);
                topList.add(new RankEntry(entrykey, value));
            }
            return topList;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    /**
     * 根据排名范围随机取排行榜内size个元素
     */
    public Collection<RankEntry> randomEntry(String key, int min, int max, int size) {
        RScoredSortedSet<String> zset = touch();
        Integer rank = zset.revRank(key);
        if (rank == null) {
            return randomEntry(key, zset.entryRange(0, min + max), size);
        }
        int startIndex = rank - min;
        int endIndex = rank + max;
        startIndex = startIndex < 0 ? 0 : startIndex;
        return randomEntry(key, zset.entryRangeReversed(startIndex, endIndex), size);
    }

    //竞技场的对手获取规则
    public Collection<RankEntry> randomList(String key, int min, int max, int size) {
        RScoredSortedSet<String> zset = touch();
        Integer rank = zset.revRank(key);
        //自己没有排名  取0  到  min+max size个对手
        if (rank == null) return randomEntry(key, zset.entryRange(0, min + max), size);
        ArrayList<RankEntry> entrylist = new ArrayList<>();

        //取右边2个
        int leftB = zset.size() - rank;
        int rightB = zset.size() - rank + 4;
        Collection<RankEntry> listB = randomEntry(key, zset.entryRange(leftB, rightB), 2);

        //取左边2个
        int leftA = zset.size() - rank - 1 - 4 < 0 ? 0 : zset.size() - rank - 1 - 4;
        int rightA = zset.size() - rank - 1;
        Collection<RankEntry> listA = randomEntry(key, zset.entryRange(leftA, rightA), 2);

        entrylist.addAll(listB);
        entrylist.addAll(listA);

        //再取左边的随机一个，但不能与之前的相同
        if (zset.size() - leftA > 0) {
            Collection<RankEntry> listC = randomEntry(key, zset.entryRange(0, zset.size() - leftA), 1);
            entrylist.addAll(listC);
        }
        //不足的通过战力排行榜补充
        return entrylist;
    }


    private Collection<RankEntry> randomEntry(String key, Collection<ScoredEntry<String>> entrys, int size) {
        ArrayList<RankEntry> entrylist = new ArrayList<>();
        if (entrys.size() <= size) {
            for (ScoredEntry<String> entry : entrys) {
                String entrykey = entry.getValue();
                if (entrykey.equals(key)) {
                    continue;
                }
                long score = entry.getScore().longValue();
                long value = valueRestore(score);
                entrylist.add(new RankEntry(entrykey, value));
            }
            return entrylist;
        }
        ArrayList<ScoredEntry<String>> list = new ArrayList<>(entrys);
        Collections.shuffle(list);
        for (ScoredEntry<String> entry : list) {
            String entrykey = entry.getValue();
            if (entrykey.equals(key)) {
                continue;
            }
            long score = entry.getScore().longValue();
            long value = valueRestore(score);
            entrylist.add(new RankEntry(entrykey, value));
            if (entrylist.size() >= size) {
                break;
            }
        }
        return entrylist;
    }

    /**
     * 获取排名范围内的对象
     */
    public Collection<RankEntry> range(int rankStart, int rankEnd) {
        try {
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entrys = zset.entryRangeReversed(rankStart - 1, rankEnd - 1);
            ArrayList<RankEntry> topList = new ArrayList<>();
            for (ScoredEntry<String> entry : entrys) {
                String entrykey = entry.getValue();
                long score = entry.getScore().longValue();
                long value = valueRestore(score);
                topList.add(new RankEntry(entrykey, value));
            }
            return topList;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    /**
     * 获取分数范围内的对象
     */
    public Collection<RankEntry> range(long scoreStart, long scoreEnd) {
        try {
            RScoredSortedSet<String> zset = touch();
            long start = valuePlastic(scoreStart);
            long end = valuePlastic(scoreEnd);
            Collection<ScoredEntry<String>> entrys = zset.entryRangeReversed(start, true, end, true);
            ArrayList<RankEntry> topList = new ArrayList<>();
            for (ScoredEntry<String> entry : entrys) {
                String entrykey = entry.getValue();
                long score = entry.getScore().longValue();
                long value = valueRestore(score);
                topList.add(new RankEntry(entrykey, value));
            }
            return topList;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    public Collection<String> allKeys() {
        try {
            RScoredSortedSet<String> zset = touch();
            return zset.readAll();
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    public Collection<RankEntry> all() {
        try {
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entrys = zset.entryRange(0, -1);
            ArrayList<RankEntry> topList = new ArrayList<>();
            for (ScoredEntry<String> entry : entrys) {
                String entrykey = entry.getValue();
                long score = entry.getScore().longValue();
                long value = valueRestore(score);
                topList.add(new RankEntry(entrykey, value));
            }
            return topList;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    public Collection<RankEntry> allFlashback() {
        try {
            RScoredSortedSet<String> zset = touch();
            Collection<ScoredEntry<String>> entrys = zset.entryRangeReversed(0, -1);
            ArrayList<RankEntry> topList = new ArrayList<>();
            for (ScoredEntry<String> entry : entrys) {
                String entrykey = entry.getValue();
                long score = entry.getScore().longValue();
                long value = valueRestore(score);
                topList.add(new RankEntry(entrykey, value));
            }
            return topList;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }


    public int size() {
        try {
            RScoredSortedSet<String> zset = touch();
            return zset.size();
        } catch (Exception e) {
            return 0;
        }
    }

    public void remove(String key) {
        try {
            if (tryLock()) {
                RScoredSortedSet<String> zset = touch();
                zset.remove(key);
            }
        } catch (Exception e) {
        } finally {
            unlock();
        }
    }

    public void removeAll(Collection<String> keys) {
        try {
            if (tryLock()) {
                RScoredSortedSet<String> zset = touch();
                zset.removeAll(keys);
            }
        } catch (Exception e) {
        } finally {
            unlock();
        }
    }

    public void clear() {
        try {
            if (tryLock()) {
                touch().clear();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            unlock();
        }
    }

    /**
     * 导入其他排行榜数据 仅限合区使用
     */
    public void importData(RankModule rankModule) {
        try {
            if (tryLock() && rankModule.tryLock()) {
                RScoredSortedSet<String> zset = touch();

                RScoredSortedSet<String> zsetImport = rankModule.touch();
                Collection<ScoredEntry<String>> entrys = zsetImport.entryRange(0, -1);
                for (ScoredEntry<String> entry : entrys) {
                    zset.add(entry.getScore(), entry.getValue());
                }
            }
        } catch (Exception e) {
        } finally {
            unlock();
        }
    }

    // 合区后，所有区的排行榜重新调整（排行榜中的用户迁移）
    public void mergeArea () {

    }

    // lock==========================================================================

    @Override
    public String lockKey() {
        return String.format("lock:%s", rankInfo.getRankKey());
    }

    @Override
    protected RLock rLock() {
        if (lock == null) {
            lock = redis().client().getFairLock(lockKey());
        }
        return lock;
    }

}
